package cn.changein.framework.elasticsearch.respository;


import cn.changein.framework.elasticsearch.document.ArticleDoc;
import cn.changein.framework.elasticsearch.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchHit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * elasticsearch
 */
@Slf4j
@Repository
public class EsArticleRepository {
    @Autowired
    RestHighLevelClient client;

    @PostConstruct
    public void init() {
        Document document = getDocument(ArticleDoc.class);
        GetIndexRequest request = new GetIndexRequest(document.indexName());
        try {
            boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
            if (!exists) {
                CreateIndexRequest request2 = new CreateIndexRequest(document.indexName());
                request2.settings(Settings.builder()
                        .put("index.number_of_shards", document.shards())
                        .put("index.number_of_replicas", document.replicas())
                );

                Map<String, Object> properties = new HashMap<>();
                for (java.lang.reflect.Field field : ArticleDoc.class.getDeclaredFields()) {
                    Field singleField = field.getAnnotation(Field.class);
                    if (singleField != null) {
                        Map<String, Object> item = new HashMap<>();
                        item.put("type", singleField.type().name().toLowerCase());
                        if (StringUtils.hasText(singleField.analyzer())) {
                            item.put("analyzer", singleField.analyzer());
                            if (StringUtils.hasText(singleField.searchAnalyzer()))
                                item.put("search_analyzer", singleField.searchAnalyzer());
                        }
                        properties.put(field.getName(), item);
                    }
                }

                Map<String, Object> mapping = new HashMap<>();
                mapping.put("properties", properties);
                request2.mapping(mapping);
                CreateIndexResponse createIndexResponse = client.indices().create(request2, RequestOptions.DEFAULT);
                log.warn("create index:{}", createIndexResponse.index());
            }
        } catch (Exception e) {
            log.error("create index error:", e);
        }

    }

    public BulkResponse saveAll(List<ArticleDoc> entities, boolean docAsUpsert) throws IOException {
        BulkRequest request = new BulkRequest();
        String index = getIndex(entities.get(0).getClass());
        for (ArticleDoc item : entities) {
            String json = JsonUtil.toJson(item);
            Map<String, Object> map = JsonUtil.parseMap(json);
            UpdateRequest ur = new UpdateRequest(index, item.getId().toString());
            ur.doc(map);
            ur.docAsUpsert(docAsUpsert);
            request.add(ur);
        }
        BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);
        return response;
    }

    public BulkResponse batchDelete(List<Long> idList) throws IOException {
        BulkRequest request = new BulkRequest();
        String index = getIndex(ArticleDoc.class);
        for (Long id : idList) {
            request.add(new DeleteRequest(index, id.toString()));
        }
        BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);
        return response;
    }


    private String getIndex(Class clz) {
        return getDocument(clz).indexName();
    }

    private Document getDocument(Class clz) {
        return (Document) clz.getAnnotation(Document.class);
    }

    public Page<ArticleDoc> search(SearchRequest request, int pageNo, int pageSize) throws IOException {
        request.indices(getIndex(ArticleDoc.class));
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        List<ArticleDoc> list = new ArrayList<>();
        long total = 0;
        if (response.getHits() != null) {
            if (response.getHits().getTotalHits() != null)
                total = response.getHits().getTotalHits().value;
            if (response.getHits().getHits() != null && response.getHits().getHits().length > 0) {
                for (SearchHit hit : response.getHits().getHits()) {
                    String source = hit.getSourceAsString();

                    ArticleDoc doc = JsonUtil.parse(source, ArticleDoc.class);
                    list.add(doc);
                }
            }
        }
        Pageable pageable = PageRequest.of(pageNo, pageSize);
        return new PageImpl(list, pageable, total);
    }

    public BulkByScrollResponse updateByQueryAndScript(QueryBuilder query, String scriptStr) throws IOException {
        String index = getIndex(ArticleDoc.class);
        UpdateByQueryRequest uqr = new UpdateByQueryRequest(index);
        uqr.setQuery(query);
        Script script = new Script(scriptStr);
        uqr.setScript(script);
        BulkByScrollResponse response = client.updateByQuery(uqr, RequestOptions.DEFAULT);
        return response;
    }

    public BulkResponse updateByScript(List<Long> idList, List<String> scriptList) throws IOException {
        BulkRequest request = new BulkRequest();
        String index = getIndex(ArticleDoc.class);
        for (int i = 0; i < idList.size(); i++) {
            String scriptStr = scriptList.get(i);
            UpdateRequest ur = new UpdateRequest(index, idList.get(i).toString());
            ur.script(new Script(scriptStr));
            request.add(ur);
        }
        BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);
        return response;
    }

    public void deleteIndex() throws IOException {
        Document document = getDocument(ArticleDoc.class);
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(document.indexName());
        client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        init();
    }
}
